package net.simflow.swf.analyzer.network.data;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;

import net.simflow.swf.analyzer.exec.data.NodeTask;

public class Node implements ElementInNetwork {

	private String nameInNetwork;

	private Service service;
	private HashSet<Node> parentDataSet = new HashSet<Node>();
	private HashSet<Node> childDataSet = new HashSet<Node>();
	private HashSet<Node> parentControlSet = new HashSet<Node>();
	private HashSet<Node> childControlSet = new HashSet<Node>();

	public String getNameInNetwork() {
		return nameInNetwork;
	}

	public void setNameInNetwork(String nameInNetwork) {
		this.nameInNetwork = nameInNetwork;
	}

	public Node(Service service) {
		this.service = service;
	}

	public Service getService() {
		return service;
	}

	public HashSet<Node> getParentDataSet() {
		return parentDataSet;
	}

	public HashSet<Node> getChildDataSet() {
		return childDataSet;
	}

	public HashSet<Node> getParentControlSet() {
		return parentControlSet;
	}

	public HashSet<Node> getChildControlSet() {
		return childControlSet;
	}

	public void setService(Service service) {
		this.service = service;
	}

	public boolean addParentData(Node parent) {
		return this.parentDataSet.add(parent);
	}

	public boolean addParentDataSet(Collection<? extends Node> parents) {
		return this.parentDataSet.addAll(parents);
	}

	public boolean addChildData(Node child) {
		return this.childDataSet.add(child);
	}

	public boolean addChildDataSet(Collection<? extends Node> children) {
		return this.childDataSet.addAll(children);
	}

	public boolean addParentControl(Node parent) {
		return this.parentControlSet.add(parent);
	}

	public boolean addParentControlSet(Collection<? extends Node> parents) {
		return this.parentControlSet.addAll(parents);
	}

	public boolean addChildControl(Node child) {
		return this.childControlSet.add(child);
	}

	public boolean addChildControlSet(Collection<? extends Node> children) {
		return this.childControlSet.addAll(children);
	}

	public int getChildCount() {
		HashSet<Node> childSet = new HashSet<Node>();
		childSet.addAll(childDataSet);
		childSet.addAll(childControlSet);
		return childSet.size();
	}

	public int getParentCount() {
		HashSet<Node> parentSet = new HashSet<Node>();
		parentSet.addAll(parentDataSet);
		parentSet.addAll(parentControlSet);
		return parentSet.size();
	}

	public int getLinkCount() {
		return this.childDataSet.size() + this.childControlSet.size()
				+ this.parentDataSet.size() + this.parentControlSet.size();
	}

	public String getShortInfo() {
		return this.getNameInNetwork() + "(" + this.getService().getShortInfo()
				+ ")";
	}

	public String getFullInfo() {
		return this.getNameInNetwork() + "(" + this.getService().getFullInfo()
				+ ")";
	}

	@Override
	public String toString() {
		return getFullInfo();
	}

	private HashMap<Integer, NodeTask> waitingTaskMap = new HashMap<Integer, NodeTask>();
	private LinkedList<NodeTask> preparedTaskList = new LinkedList<NodeTask>();
	private Flow flow;

	public void initExec(int serviceAbility, int totalStep, Flow flow,
			int schedule) {
		this.waitingTaskMap.clear();
		this.preparedTaskList.clear();
		this.flow = flow;
		this.getService().initExec(serviceAbility, totalStep, schedule);
	}

	public void submitPreparedTasks(int time) {
		for (NodeTask task : preparedTaskList) {
			this.getService().appendTask(task.getId(), this, time,
					task.getFlowStartTime(), task.getFlowDeadline());
		}
		preparedTaskList.clear();
	}

	public void addTask(int taskId, Node node, boolean isControl,
			int flowStartTime, int flowDeadline) {
		if (node == null) {
			preparedTaskList.add(new NodeTask(taskId, flowStartTime,
					flowDeadline));
			return;
		}

		NodeTask task = waitingTaskMap.get(taskId);
		if (task == null) {
			task = new NodeTask(taskId, flowStartTime, flowDeadline);
			waitingTaskMap.put(taskId, task);
		}

		if (this.getParentDataSet().contains(node) && !isControl) {
			task.getParentNodeDataSet().add(node);
		}
		if (this.getParentControlSet().contains(node) && isControl) {
			task.getParentNodeControlSet().add(node);
		}
		if (task.getParentNodeControlSet().size() == this.getParentControlSet()
				.size()
				&& task.getParentNodeDataSet().size() == this
						.getParentDataSet().size()) {
			waitingTaskMap.remove(task);
			preparedTaskList.add(task);
		}
	}

	public void returnTask(int taskId, int flowStartTime, int flowDeadline) {
		for (Node child : this.getChildControlSet()) {
			child.addTask(taskId, this, true, flowStartTime, flowDeadline);
		}
		for (Node child : this.getChildDataSet()) {
			child.addTask(taskId, this, false, flowStartTime, flowDeadline);
		}
	}

	public void removeAllTask(HashSet<Integer> taskIdSet) {
		for (int tid : taskIdSet) {
			waitingTaskMap.remove(tid);
		}
	}

	public void returnMishandledTask(int taskId, int time, Service svc) {
		this.flow.returnMishandledTask(taskId, time, svc);
	}
}
